﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;

namespace Remote_Commander
{
    public struct CommandParameters
    {
        public string remoteCommand
        {
            get;
            set;
        }

        public int timeoutSeconds
        {
            get;
            set;
        }

        public bool interactiveCommand
        {
            get;
            set;
        }

        public string domainUsername
        {
            get;
            set;
        }

        public string password
        {
            get;
            set;
        }

        public List<string> selectedComputers
        {
            get;
            set;
        }
    }

    public partial class frm_MainWindow : Form
    {
        //Forms we will work with
        frm_connectToAD frm_ADWindow = new frm_connectToAD();
        frm_computerFilter frm_filterWindow = new frm_computerFilter();
        frm_WaitBox frm_waitWindow;
        frm_output frm_outputWindow;

        //Active Directory principal object
        PrincipalContext ActiveDirectoryConnection;

        //Comand parameters object, to be passed to the output form
        CommandParameters parameters = new CommandParameters();

        //Domain controller to connect to
        string strDomainController;
        bool connectionSuccessful;

        //Filter options
        bool showAllComputers;
        string filterGroupName;

        //Other variables
        int checkedNodes = 0;

        //Constructor
        public frm_MainWindow()
        {
            InitializeComponent();

            this.Show();

            BindToController();
            
        }

        //Connect to a domain controller, set filter options, and display computers
        private void BindToController()
        {
            //Display the connection dialog
            frm_ADWindow.ShowDialog();

            //Store the boolean value of a successful connection
            connectionSuccessful = frm_ADWindow.connectionSuccessful;

            if (connectionSuccessful)
            {
                clearSearchResults();

                getFilterOptions();

                //Display the connection status
                strDomainController = frm_ADWindow.strDomainController;
                lbl_connectionStatus.Text = "Connected to: " + strDomainController;

                //Store the Active Directory connection
                ActiveDirectoryConnection = frm_ADWindow.ActiveDirectoryConnection;

                frm_waitWindow = new frm_WaitBox();

                //User has chosen to view all domain computers
                if (showAllComputers)
                {                
                    //Create a new computer principal, and set it's "enabled" property to "true"
                    //This will only show us computers that haven't been disabled.
                    ComputerPrincipal CP = new ComputerPrincipal(ActiveDirectoryConnection);
                    CP.Enabled = true;

                    //Create a new principal searcher, and set its filter to match the 
                    //computer principal we created
                    PrincipalSearcher searcher = new PrincipalSearcher(CP);
                    searcher.QueryFilter = CP;

                    //Show the wait dialog, and populate the treeview

                    frm_waitWindow.Show();
                    ShowAllComputers(searcher);
                    frm_waitWindow.Close();
                }
                else
                {
                    //Find the group that the user entered
                    GroupPrincipal GP = GroupPrincipal.FindByIdentity(ActiveDirectoryConnection, filterGroupName);

                    if (GP == null)
                    {
                        //No group found.  Show an error.
                        MessageBox.Show("Error: No group found by that name.\r\nPlease check your spelling and try again.");
                    }
                    else
                    {
                        //Get the members of the group
                        PrincipalSearchResult<Principal> groupMembers = GP.GetMembers();
                        
                        //Show the wait dialog, and populate the treeview
                        frm_waitWindow.Show();
                        ShowGroupMembers(groupMembers);
                        frm_waitWindow.Close();
                    }
                }

                //Display the main application area
                pnl_appArea.Visible = true;

            }
        }

        //Get the user's filter preferences
        private void getFilterOptions()
        {
            //Display the search filter dialog
            frm_filterWindow.ShowDialog();

            //Set filter options
            showAllComputers = frm_filterWindow.allComputersSelected;
            filterGroupName = frm_filterWindow.groupName;
        }

        //Dispose of the current search results
        private void clearSearchResults()
        {
            if (trv_ADTree.Nodes.Count > 0)
            {
                while (trv_ADTree.Nodes.Count > 0)
                {
                    trv_ADTree.Nodes[0].Remove();
                }
            }

            checkedNodes = 0;
            lbl_countSelectedComputers.Text = checkedNodes + " computers selected";
        }

        //Display all computers in the domain
        private void ShowAllComputers(PrincipalSearcher searcher)
            {
                foreach (ComputerPrincipal computer in searcher.FindAll())
                {
                    TreeNode computerNode = new TreeNode(computer.Name);

                    trv_ADTree.Nodes.Add(computerNode);

                
                }

                trv_ADTree.Sort();
            }

        //Display members of an Active Directory group
        private void ShowGroupMembers(PrincipalSearchResult<Principal> groupMembers)
        {
            foreach (ComputerPrincipal computer in groupMembers)
            {
                TreeNode computerNode = new TreeNode(computer.Name);

                trv_ADTree.Nodes.Add(computerNode);
            }

            trv_ADTree.Sort();

            lbl_connectionStatus.Text += " - Group: " + filterGroupName;
        }

        //Start a new connection
        private void newConnectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            BindToController();
        }

        //Quit the program
        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        //Display the "About" dialog
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox frm_about = new AboutBox();
            frm_about.ShowDialog();
        }

        //Disconnect from the current domain controller
        private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            lbl_connectionStatus.Text = "Not connected";
            pnl_appArea.Visible = false;
            clearSearchResults();
            ActiveDirectoryConnection.Dispose();
        }

        //Update the count of checked computers
        private void trv_ADTree_AfterCheck(object sender, TreeViewEventArgs e)
        {
            checkedNodes = 0;

            foreach (TreeNode node in trv_ADTree.Nodes)
            {
                if (node.Checked)
                {
                    checkedNodes++;
                }
            }

            lbl_countSelectedComputers.Text = checkedNodes + " computers selected.";
        }

        //Manage the hotkey combinations for the treeview
        private void trv_ADTree_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode.ToString() == "A")
            {
                if (checkedNodes < trv_ADTree.Nodes.Count)
                {
                    foreach (TreeNode node in trv_ADTree.Nodes)
                    {
                        if (!node.Checked)
                        {
                            node.Checked = true;
                        }
                    }
                }
                else
                {
                    foreach (TreeNode node in trv_ADTree.Nodes)
                    {
                        if (node.Checked)
                        {
                            node.Checked = false;
                        }
                    }
                }
            }
        }

        //Submit and run the command data
        private void btn_submitCommand_Click(object sender, EventArgs e)
        {
            double d = 0;

            if (txt_remoteCommand.Text == string.Empty)
            {
                MessageBox.Show("Error: You must supply a command.");
            }
            else if (!Double.TryParse(txt_timeoutSeconds.Text, out d))
            {
                MessageBox.Show("Error: You must enter a valid integer.");
            }
            else if (txt_domainUsername.Text == string.Empty)
            {
                MessageBox.Show("Error: You must supply a valid domain and username.");
            }
            else if (txt_password.Text == string.Empty)
            {
                MessageBox.Show("Error: You must supply a password.");
            }
            else if (checkedNodes == 0)
            {
                MessageBox.Show("Error: You must select at least one computer.");
            }
            else
            {
                //Store the commands
                parameters.remoteCommand = txt_remoteCommand.Text;
                parameters.timeoutSeconds = Convert.ToInt32(txt_timeoutSeconds.Text);
                parameters.interactiveCommand = chk_interactiveMode.Checked;
                parameters.domainUsername = txt_domainUsername.Text;
                parameters.password = txt_password.Text;
                parameters.selectedComputers = new List<string>();

                //Add the checked computers to the list
                foreach (TreeNode node in trv_ADTree.Nodes)
                {
                    if (node.Checked)
                    {
                        parameters.selectedComputers.Add(node.Text);
                    }
                }

                //Show the output dialog and begin running commands
                frm_outputWindow = new frm_output(parameters);
                frm_outputWindow.ShowDialog();
            }
        }
    }
}
